#include "PBuffer.h"

#include <AGL/agl.h>
#include <OpenGL/OpenGL.h>

GLuint format = GL_TEXTURE_RECTANGLE_EXT;
//GLuint format = GL_TEXTURE_2D;

static void checkCGLErrorImplementation(CGLError error, char* sourceFile,
		int sourceLine) {
	if (error) {
		const char* errStr;
		errStr = CGLErrorString(error);
		fprintf(stderr, "CGL Error: %s at %s:%d\n", errStr, sourceFile,
				sourceLine);
		assert(false);
	}
}

#define checkCGLError(error) checkCGLErrorImplementation(error,__FILE__,__LINE__)

PetePBuffer* PBuffer_Create(CGLContextObj sharedContext, int width, int height,
		int colorDepth, int flags) {
	//  printf("width %d, height %d\n", width, height);
	PetePBuffer* pbuffer=new PetePBuffer;
	pbuffer->pbuffer=NULL;
	pbuffer->pbufferContext=NULL;
	pbuffer->previousContext=NULL;
	pbuffer->width=width;
	pbuffer->height=height;
	pbuffer->textureID=0;
	pbuffer->needsClearing=true;
	pbuffer->needsFlush=false;

	const int bitsPerPixel = (colorDepth*4);
	//const int bitsPerPixel = (colorDepth);
	const bool hasZBuffer = (flags&ePBufferFlag_ZBuffer);

	int i = 0;
	CGLPixelFormatAttribute pixelFormatAttributes[32];

	pixelFormatAttributes[i++] = kCGLPFARobust;
	pixelFormatAttributes[i++] = kCGLPFANoRecovery;
	pixelFormatAttributes[i++] = kCGLPFADoubleBuffer;
	//pixelFormatAttributes[i++] = kCGLPFAAccelerated;
	//pixelFormatAttributes[i++] = kCGLPFAWindow;
	pixelFormatAttributes[i++] = kCGLPFAColorSize;
	pixelFormatAttributes[i++] = (CGLPixelFormatAttribute)(bitsPerPixel);

	if (colorDepth>8)
		pixelFormatAttributes[i++] = kCGLPFAColorFloat;

	if (hasZBuffer) {
		pixelFormatAttributes[i++] = kCGLPFADepthSize;
		pixelFormatAttributes[i++] = (CGLPixelFormatAttribute)(16);
	}

	pixelFormatAttributes[i++] = (CGLPixelFormatAttribute)0;

	long numPixelFormats = 0;
	CGLPixelFormatObj pixelFormat = NULL;
	CGLError error = CGLChoosePixelFormat(pixelFormatAttributes, &pixelFormat,
			&numPixelFormats);
	checkCGLError(error);

	error = CGLCreateContext(pixelFormat, sharedContext,
			&pbuffer->pbufferContext);
	checkCGLError(error);

	CGLDestroyPixelFormat(pixelFormat);

	error = CGLCreatePBuffer(pbuffer->width, pbuffer->height, format, GL_RGBA,
			0, &pbuffer->pbuffer);
	checkCGLError(error);

	return pbuffer;

}

void PBuffer_Destroy(PetePBuffer* pbuffer) {

	if (pbuffer!=NULL) {
		CGLDestroyPBuffer(pbuffer->pbuffer);
		CGLDestroyContext(pbuffer->pbufferContext);
	}

	delete pbuffer;

}

void PBuffer_Begin(PetePBuffer* pbuffer) {

	// Pete- check to ensure begin() hasn't already been called for this pbuffer
	assert(pbuffer->previousContext==NULL);

	pbuffer->previousContext=CGLGetCurrentContext();

	long screen;
	CGLError error=CGLGetVirtualScreen(pbuffer->previousContext, &screen);
	assert(!error);

	error=CGLSetCurrentContext(pbuffer->pbufferContext);
	checkCGLError(error);

	error
			=CGLSetPBuffer(pbuffer->pbufferContext, pbuffer->pbuffer, 0, 0,
					screen);
	checkCGLError(error);

}

void PBuffer_End(PetePBuffer* pbuffer) {

	glFlush();

	pbuffer->needsFlush=true;

	assert(pbuffer->previousContext!=NULL);

	CGLError error=CGLSetCurrentContext(pbuffer->previousContext);
	checkCGLError(error);

	pbuffer->previousContext=NULL;

}

void PBuffer_SetTexture(PetePBuffer* pbuffer) {
	CGLContextObj currentContext=CGLGetCurrentContext();
	CGLError error=CGLTexImagePBuffer(currentContext, pbuffer->pbuffer,
			GL_FRONT_LEFT);
	checkCGLError(error);
}

void PBuffer_Use(PetePBuffer* pbuffer) {

	if (pbuffer->needsFlush) {
		CGLContextObj currentContext=CGLGetCurrentContext();

		CGLError error=CGLSetCurrentContext(pbuffer->pbufferContext);
		checkCGLError(error);

		glFlush();

		error=CGLSetCurrentContext(currentContext);
		checkCGLError(error);

		pbuffer->needsFlush=false;
	}

	if (pbuffer->textureID==0) {
		CGLContextObj currentContext=CGLGetCurrentContext();

		glGenTextures(1, &pbuffer->textureID);
		glBindTexture(format, pbuffer->textureID);
		glTexParameteri(format, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(format, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(format, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(format, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexParameteri(format, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(format, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

		CGLError error=CGLTexImagePBuffer(currentContext, pbuffer->pbuffer,
				GL_FRONT_LEFT);
		checkCGLError(error);

	} else {
		glBindTexture(format, pbuffer->textureID);
	}

	//glEnable(GL_TEXTURE_RECTANGLE_EXT);

}
